
package com.dotools.utils;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;

import com.dotools.compat.ViewMissed;
import com.dotools.debug.LOG;
import com.dotools.reflection.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class UiUtils {
    static final int MIN_HEAP_TO_ENABLE_DRAWING_CACHE = 24; // MB's
    static final int MIN_HEAP_TO_ENABLE_DRAWING_CACHE_ICS = 32; // MB's
    static final int MIN_MEMORY_CLASS = 48; // NexusS = 48MB, Galaxy Nexus = 64MB

    static final int MIN_PHYSCIAL_MEM_TO_ENABLE_DRAWING_CACHE = 256 * 1024; // KB's
    static final int MIN_PHYSCIAL_MEM_TO_ENABLE_DRAWING_CACHE_ICS = 320 * 1024; // KB's
    static final int MIN_SDK_LEVEL = 11; // android 3.0 = 11
    static final int MIN_SYSTEM_FREE_MEM_TO_ENABLE_HARDWRE_ACCELERATION = 32 * 1024 * 1024; // bytes

    static final int MIN_SYSTEM_PHYSICAL_MEM_TO_ENABLE_HARDWRE_ACCELERATION = 320 * 1024; // KBs
    // Physical Memory: Nexus One = 394360 kB, Nexus S = 342896 kB, Galaxy Nexus = 711480 kB

    private static float sDensity = 0f;
    private static int sDensityDpi = 0;
    private static int sScreenWidth = 0;
    private static int sScreenHeight = 0;

    static int sShouldEnableDrawingCache = -1;
    static int sShouldEnableHardwareAcceleration = -1;

    public static Bitmap decodeBitmapWithoutScale(int id) {
        Options op = new Options();
        op.inScaled = false;
        Resources res = Utilities.getApplicationContext().getResources();
        Bitmap bitmap = BitmapFactory.decodeResource(res, id, op);
        bitmap.setDensity(res.getDisplayMetrics().densityDpi);
        return bitmap;
    }

    /**
     * @param resources, resources may be a theme apk's resources
     * @param resId
     * @return
     */
    public static Bitmap decodeBitmap(Resources resources, int resId) {
        Options opts = new Options();
        opts.inJustDecodeBounds = false;
        opts.inDither = false;
        opts.inScaled = true;
        Bitmap bitmap = BitmapFactory.decodeResource(resources, resId, opts);
        bitmap.setDensity(Utilities.getApplicationContext().getResources().getDisplayMetrics().densityDpi);
        return bitmap;
    }

    public static void releaseHardwareAccelerationMemory() {
        if (DevicesUtils.legacyDevices()) {
            return;
        }

        // HW render should not be touched outside of UI thread
        ThreadUtils.ensureUiThread();

        // Cleanup hardware accelerated stuff
        // WindowManagerGlobal.getInstance().trimLocalMemory();
        Class<?> clsWMG = ReflectionUtils.loadClassNoThrow(UiUtils.class.getClassLoader(), "android.view.WindowManagerGlobal");
        if (clsWMG != null) {
            Method methodGetInstance = ReflectionUtils.findMethodNoThrow(clsWMG, "getInstance", new Class<?>[]{});
            Method methodTrimeLocalMemory = ReflectionUtils.findMethodNoThrow(clsWMG, "trimLocalMemory", new Class<?>[]{});

            Object wmGlobal = null;
            try {
                wmGlobal = methodGetInstance.invoke(null, (Object[]) null);
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (wmGlobal != null && methodTrimeLocalMemory != null) {
                try {
                    methodTrimeLocalMemory.invoke(wmGlobal, (Object[]) null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                LOG.logI("could not trim local memory");
            }

            Method methodStartTrimMemory = ReflectionUtils.findMethodNoThrow(clsWMG, "startTrimMemory", new Class<?>[]{int.class});
            Method methodEndTrimMemory = ReflectionUtils.findMethodNoThrow(clsWMG, "endTrimMemory", new Class<?>[]{});
            if (wmGlobal != null && methodStartTrimMemory != null && methodEndTrimMemory != null) {
                try {
                    methodStartTrimMemory.invoke(wmGlobal, 95);
                    methodEndTrimMemory.invoke(wmGlobal, (Object[]) null);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                LOG.logI("could not trim memory");
            }
        }
    }

    public static void destroyDrawingCache(final View view) {
        ThreadUtils.ensureUiThread();

        if (view == null)
            return;

        // Debug.logD(view.toString());

        view.destroyDrawingCache();
        ViewMissed.setLayerType(view, View.LAYER_TYPE_NONE, null);

        if (view instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup) view;
            int count = vg.getChildCount();
            for (int i = 0; i < count; i++) {
                View v = vg.getChildAt(i);
                if (v != null) {
                    destroyDrawingCache(v);
                }
            }
        }
    }

    public static void destroyIMM() {
        // clear reference in InputMethodManager
        try {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            Class<?> clsIMM;
            clsIMM = cl.loadClass("android.view.inputmethod.InputMethodManager");

            Field fieldInstance = clsIMM.getDeclaredField("mInstance");
            fieldInstance.setAccessible(true);
            Object instance = fieldInstance.get(null);

            Field fieldView = clsIMM.getDeclaredField("mCurRootView");
            fieldView.setAccessible(true);
            fieldView.set(instance, null);

            fieldView = clsIMM.getDeclaredField("mServedView");
            fieldView.setAccessible(true);
            fieldView.set(instance, null);

            fieldView = clsIMM.getDeclaredField("mNextServedView");
            fieldView.setAccessible(true);
            fieldView.set(instance, null);

            // LOG.logD("destroyIMM done");
        } catch (ClassNotFoundException e) {
        } catch (SecurityException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        } catch (NoSuchFieldException e) {
        } catch (NullPointerException e) {
        }
    }

    public static void destroyView(View view) {
        if (view == null)
            return;

        // set view.mContext = null, mParent = null
        try {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            Class<?> clsView;
            clsView = cl.loadClass("android.view.View");

            Field fieldContext = clsView.getDeclaredField("mContext");
            fieldContext.setAccessible(true);
            fieldContext.set(view, null);

            Field fieldParent = clsView.getDeclaredField("mParent");
            fieldParent.setAccessible(true);
            fieldParent.set(view, null);
        } catch (ClassNotFoundException e) {
        } catch (SecurityException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        } catch (NoSuchFieldException e) {
        } catch (NullPointerException e) {
        }

        try {
            view.setVisibility(View.GONE);
            view.destroyDrawingCache();
            ViewMissed.setLayerType(view, View.LAYER_TYPE_NONE, null);

            if (view.getBackground() != null) {
                view.setBackgroundDrawable(null);
            }

            view.setAnimation(null);
            view.setOnFocusChangeListener(null);
            view.setOnKeyListener(null);
            view.setOnLongClickListener(null);
            view.setOnTouchListener(null);
            if (!(view instanceof AdapterView))
                view.setOnClickListener(null);

            if ((view instanceof ViewGroup) && !(view instanceof AdapterView)) {
                ViewGroup vg = (ViewGroup) view;
                int count = vg.getChildCount();
                for (int i = 0; i < count; i++) {
                    View v = vg.getChildAt(i);
                    if (v != null)
                        destroyView(v);
                }
                vg.removeAllViews();
            } else if (view instanceof AdapterView) {
                AdapterView<?> av = (AdapterView<?>) view;
                av.setAdapter(null);
                av.setAnimation(null);
                av.setOnItemClickListener(null);
                av.setOnItemSelectedListener(null);
                av.setOnItemLongClickListener(null);
            }
        } catch (Exception e) {
            // silently discard all errors
        }
    }

    public static int dipToPx(int dip) {
        return (int) (dip * getDensity() + 0.5f);
    }

    public static float dipToPxFloat(int dip) {
        return (dip * getDensity() + 0.5f);
    }

    public static void disableHardwareAcceleration() {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
            return;
        }

        // trying to invoke HardwareRenderer.disable(true);

        try {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            Class<?> cls = cl.loadClass("android.view.HardwareRenderer");
            if (cls != null) {
                Method methodDisable = cls.getDeclaredMethod("disable", new Class<?>[]{
                        boolean.class
                });
                methodDisable.setAccessible(true);
                methodDisable.invoke(null, true);
            }
        } catch (SecurityException e) {
        } catch (ClassNotFoundException e) {
        } catch (NoSuchMethodException e) {
        } catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        } catch (InvocationTargetException e) {
        }
    }


    /**
     * 将dp值转换为px值
     */
    public static int dip2px(Context context, Float dpValue) {
        Float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public static float getDensity() {
        if (sDensity == 0f) {
            DisplayMetrics dm = new DisplayMetrics();
            ((WindowManager) Utilities.getApplicationContext().getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay().getMetrics(dm);
            sDensity = dm.density;
        }
        return sDensity;
    }

    public static int getDensityDpi() {
        if (sDensityDpi == 0) {
            DisplayMetrics dm = new DisplayMetrics();
            ((WindowManager) Utilities.getApplicationContext().getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay().getMetrics(dm);
            sDensityDpi = dm.densityDpi;
        }
        return sDensityDpi;
    }

    private static synchronized void initScreenHeightWidth() {
        DisplayMetrics dm = new DisplayMetrics();
        ((WindowManager) Utilities.getApplicationContext().getSystemService(Context.WINDOW_SERVICE))
                .getDefaultDisplay().getMetrics(dm);
        sScreenWidth = dm.widthPixels;
        sScreenHeight = dm.heightPixels;
        if (sScreenHeight < sScreenWidth) {
            int t = sScreenHeight;
            sScreenHeight = sScreenWidth;
            sScreenWidth = t;
        }
    }

    public static int getScreenWidthPixels() {
        if (sScreenWidth == 0) {
            initScreenHeightWidth();
        }
        return sScreenWidth;
    }


    public static int getScreenHeightPixels() {
        if (sScreenHeight == 0) {
            initScreenHeightWidth();
        }
        return sScreenHeight;
    }


    static public int pxToDip(int px) {
        return (int) (px / getDensity() + 0.5f);
    }

    public static Bitmap scaleBitmap(Bitmap bitmap, int w, int h, Config config) {

        if (bitmap == null || bitmap.isRecycled()) {
            return null;
        }
        if (bitmap.getWidth() == w && bitmap.getHeight() == h) {
            return bitmap;
        }
        if (bitmap != null && w > 0 && h > 0 && config != null) {
            Bitmap newBitmap = Bitmap.createBitmap(w, h, config);
            newBitmap.setDensity(bitmap.getDensity());
            Canvas canvas = new Canvas(newBitmap);
            Paint paint = new Paint();
            paint.setDither(true);
            paint.setAntiAlias(true);
            canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), new Rect(0, 0, w, h), paint);
            bitmap.recycle();
            return newBitmap;
        } else {
            return bitmap;
        }
    }

    public static void setDrawingParameters(View v, boolean enabled) {
        if (v != null) {
            v.setWillNotDraw(!enabled);
            v.setDrawingCacheEnabled(enabled);
            v.setWillNotCacheDrawing(!enabled);

            if (v instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) v;
                vg.setAlwaysDrawnWithCacheEnabled(enabled);
            }
        }
    }

    public static boolean shouldEnableDrawingCache() {
        if (sShouldEnableDrawingCache < 0) {
            if (shouldEnableHardwareAcceleration()) {
                sShouldEnableDrawingCache = 0; // let display hardware handle optimization
            } else {
                ActivityManager am = null;
                if (am == null) {
                    am = (ActivityManager) Utilities.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
                }
                int memClass = am.getMemoryClass();
                if (Build.VERSION.SDK_INT < MIN_SDK_LEVEL) {
                    // low end: enable depend on physical memory; memory consumption is lower, and FPS is lower
                    boolean bEnoughMemory = DevicesUtils.getPhysicalMemoryKBs() > MIN_PHYSCIAL_MEM_TO_ENABLE_DRAWING_CACHE
                            && memClass >= MIN_HEAP_TO_ENABLE_DRAWING_CACHE;
                    sShouldEnableDrawingCache = bEnoughMemory ? 1 : 0;
                } else {
                    // low end ics devices
                    boolean bEnoughMemory = DevicesUtils.getPhysicalMemoryKBs() > MIN_PHYSCIAL_MEM_TO_ENABLE_DRAWING_CACHE_ICS
                            && memClass >= MIN_HEAP_TO_ENABLE_DRAWING_CACHE_ICS;
                    sShouldEnableDrawingCache = bEnoughMemory ? 1 : 0;
                }
            }
        }
        return (sShouldEnableDrawingCache > 0);
    }

    public static boolean shouldEnableHardwareAcceleration() {
        if (sShouldEnableHardwareAcceleration < 0) {
            ActivityManager am = null;
            if (am == null) {
                am = (ActivityManager) Utilities.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
            }
            sShouldEnableHardwareAcceleration = 0;
            // at least android 3.0 devices
            if (Build.VERSION.SDK_INT >= MIN_SDK_LEVEL) {
                if (DevicesUtils.getPhysicalMemoryKBs() > MIN_SYSTEM_PHYSICAL_MEM_TO_ENABLE_HARDWRE_ACCELERATION) {
                    MemoryInfo memInfo = new MemoryInfo();
                    // plenty of RAM & heap size
                    int memClass = am.getMemoryClass();
                    if (memClass >= MIN_MEMORY_CLASS) {
                        am.getMemoryInfo(memInfo);
                        if (memInfo.availMem > MIN_SYSTEM_FREE_MEM_TO_ENABLE_HARDWRE_ACCELERATION) {
                            // Debug.logD("available:" + memInfo.availMem);
                            sShouldEnableHardwareAcceleration = 1;
                        }
                    }
                }
            }
        }
        return (sShouldEnableHardwareAcceleration > 0);
    }

    public static int spToPx(float sp) {
        return (int) (sp * getDensity() + 0.5f);
    }

    private static Rect sRectStatusBar = new Rect();

    public static int getStatusBarHeight(Activity act) {
        if (act != null) {
            act.getWindow().getDecorView().getWindowVisibleDisplayFrame(sRectStatusBar);
            return (sRectStatusBar.top > 0) ? sRectStatusBar.top : 0;
        } else {
            return 0;
        }
    }

    public static void onConfigurationChanged(Configuration newConfig) {
        sDensity = 0f;
        sDensityDpi = 0;
        sScreenWidth = 0;
        sScreenHeight = 0;
    }

    private static double mScreenInches = 0;

    public static double getScreenSizeOfDevice() {
        if (mScreenInches == 0) {
            int width = getScreenWidthPixels();
            int height = getScreenHeightPixels();
            float density = getDensity();
            double diagonalPixels = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
            mScreenInches = diagonalPixels / (160 * density);
        }
        return mScreenInches;
    }


    private static Integer mRealScreenHeight = 0;

    /**
     * 获得设备在竖屏时的物理高度（包括虚拟按键，通知栏的高度)
     *
     * @param display
     * @return
     */
    @SuppressLint("NewApi")
    public static int getScreenHeightPixelsTotal() {

        if (mRealScreenHeight == 0) {
            if (DevicesUtils_vk.hasVertualKey()) {
                Display display = ((WindowManager) Utilities.getApplicationContext().getSystemService(Context.WINDOW_SERVICE))
                        .getDefaultDisplay();
                int orientation = display.getRotation();
                boolean landscape = orientation == Surface.ROTATION_270 || orientation == Surface.ROTATION_90;
                Point size = new Point();
                if (Build.VERSION.SDK_INT >= 17) {
                    display.getRealSize(size);
                    mRealScreenHeight = landscape ? size.x : size.y;
                } else {
                    try {
                        Method getRawH = landscape ? Display.class.getMethod("getRawWidth") : Display.class.getMethod("getRawHeight");
                        mRealScreenHeight = (Integer) getRawH.invoke(display);
                    } catch (Exception e) {
                        display.getSize(size);
                        mRealScreenHeight = landscape ? size.x : size.y;
                    }
                }
            }
            if (mRealScreenHeight == 0) {
                mRealScreenHeight = UiUtils.getScreenHeightPixels();
            }
        }
        return mRealScreenHeight;
    }

//        getScreenHeightPixelsTotal() {
//        if (mRealScreenHeight == null) {
//            Context applicationContext = Utilities.getApplicationContext();
//            Object systemService2 = applicationContext.getSystemService(Context.WINDOW_SERVICE);
//            WindowManager systemService = (WindowManager) systemService2;
//            Display display = systemService.getDefaultDisplay();
//            if (mRealScreenHeight != -1) {
//                return mRealScreenHeight;
//            }
//            int orientation = display.getRotation();
//            boolean landscape = orientation == Surface.ROTATION_270 || orientation == Surface.ROTATION_90;
//            Point size = new Point();
//            if (Build.VERSION.SDK_INT >= 17) {
//                display.getRealSize(size);
//                mRealScreenHeight = landscape ? size.x : size.y;
//            } else {
//                try {
//                    Method getRawH = landscape ? Display.class.getMethod("getRawWidth") : Display.class.getMethod("getRawHeight");
//                    mRealScreenHeight = (Integer) getRawH.invoke(display);
//                } catch (Exception e) {
//                    display.getSize(size);
//                    mRealScreenHeight = landscape ? size.x : size.y;
//                }
//            }
//            if(mRealScreenHeight == null || mRealScreenHeight == 0) {
//                mRealScreenHeight = getScreenHeightPixels();
//            }
//        }
//        return mRealScreenHeight;
//    }
}
